#include "UDPManager.h"
#include "UserManager.h"
#include "../MainServer.h"

#include <QUdpSocket>
#include <QImage>

void UDPManager::receiveData(const QByteArray& raw_data, const QHostAddress& sender, quint16 senderPort) {
    if (recvPing(raw_data)) {
        sendMessage(raw_data, sender, senderPort);
        return;
    }

    MSG msg = MessageHelper::makeMessage(raw_data);
    MSG::Type type = msg.type;

    if (type == MSG::CREATE) {
        quint16 roomId = RoomManager::instance()->createRoom();
        sendMessage(makeCreatedMsg(roomId), sender, senderPort);
    }
    else if (type == MSG::DELETE) {
        quint16 roomId = msg.data.toUInt();
        RoomManager::instance()->deleteRoom(roomId);
        UserManager::instance()->UserBroadcast(msg.userId, raw_data);
    }
    else if (type == MSG::JOIN) {
        quint16 userId = msg.userId;
        quint16 roomId = msg.data.toUInt();
        AbstractUser* user = UserManager::instance()->newUser(userId, sender, senderPort);
        AbstractRoom* room;
        if(RoomManager::instance()->addClientToRoom(roomId, user, room)) {
            sendMessage(makeJoinedMsg(room, userId), sender, senderPort);
            room->broadcast(makeOtherJoinedMsg(userId), userId);
        }
    }
    else if (type == MSG::LEAVE) {
        quint16 userId = msg.userId;
        quint16 roomId = msg.data.toUInt();
        AbstractRoom* room;
        if (RoomManager::instance()->removeClientFromRoom(roomId, userId, room)) {
            room->broadcast(makeOtherLeavedMsg(userId), userId);
        }
    }
    else if (type == MSG::INVITE) {
        QStringList parts = QString(msg.data).split(',');
        quint16 hostId = msg.userId;
        quint16 roomId = parts[0].toInt();
        for (int i = 1; i < parts.size(); ++i) {
            quint16 userId = parts[i].toInt();
            DbManager::instance()->addInvitation(roomId, hostId, userId);
        }
    }
    else if (type == MSG::TEXT) {
        sendMessage(raw_data, sender, senderPort);
        UserManager::instance()->UserBroadcast(msg.userId, raw_data);
    }
    else if (type == MSG::LOGIN) {
        QStringList parts = QString(msg.data).split(',');
        QString username = parts[0];
        QString password = parts[1];
        quint16 userId = 0;
        STATUS status = DbManager::instance()->checkUser(username, password, userId);
        qDebug() << userId;
        sendMessage(makeDbStatusMsg(status, userId), sender, senderPort);
    }
    else if (type == MSG::REGIETER) {
        QStringList parts = QString(msg.data).split(',');
        QString username = parts[0];
        quint32 phone = parts[1].toUInt();
        QString password = parts[2];
        quint16 userType = parts[3].toUInt();
        STATUS status = DbManager::instance()->registerUser(username, password, phone, userType);
        sendMessage(makeDbStatusMsg(status), sender, senderPort);
    }
    else if (type == MSG::QUERY) {
        if (msg.data == "HISTORY") {
            sendMessage(makeHistoryMsg(DbManager::instance()->queryUserHistoryWorkOrders(msg.userId)), sender, senderPort);
        } else if (msg.data == "INVITATION") {
            sendMessage(makeInvitationsMsg(DbManager::instance()->queryAllInvitations(msg.userId)), sender, senderPort);
        } else if (msg.data == "EXPERTS") {
            sendMessage(makeExpertsMsg(DbManager::instance()->queryAllUsersAlphabetical(1)), sender, senderPort);
        }
    }
    else if (type == MSG::DEL_INV) {
        quint16 invitationId = msg.data.toUInt();
        DbManager::instance()->delInvitation(invitationId);
    }
}

QByteArray UDPManager::makeCreatedMsg(quint16 roomId) {
    MSG msg;
    msg.type = MSG::CREATED;
    msg.data = QByteArray::number(roomId);
    return MessageHelper::readMessage(msg);
}

QByteArray UDPManager::makeJoinedMsg(AbstractRoom* room, quint16 excludeUserId) {
    MSG msg;
    msg.type = MSG::JOINED;
    msg.data = QByteArray::number(room->getId());
    for (quint16 uid : room->getClientIDs()) {
        if (uid == excludeUserId) continue;
        msg.data.append(',' + QByteArray::number(uid));
    }
    qDebug() << msg.data;
    return MessageHelper::readMessage(msg);
}

QByteArray UDPManager::makeOtherJoinedMsg(quint16 userId) {
    return "OTHER_JOINED 0 " + QByteArray::number(userId);
}

QByteArray UDPManager::makeOtherLeavedMsg(quint16 userId) {
    return "OTHER_LEAVED 0 " + QByteArray::number(userId);
}

QByteArray UDPManager::makeDbStatusMsg(STATUS status, const quint16 userId) {
    User user = DbManager::instance()->queryUser(userId);
    QByteArray data = "DB_STATUS " + user.toByteArray() + ' ';
    data += DbManager::getStatusString(status);
    return data;
}

QByteArray UDPManager::makeHistoryMsg(const QList<History>& historyList) {
    QByteArray data = "HISTORY 0 ";
    for (const auto& history : historyList) {
        data.append(history.toByteArray());
        data.append(';');
    }
    return data;
}

QByteArray UDPManager::makeExpertsMsg(const QList<User>& expertsList) {
    QByteArray data = "EXPERTS 0 ";
    for (const auto& expert : expertsList) {
        data.append(expert.toByteArray());
        data.append(';');
    }
    return data;
}

QByteArray UDPManager::makeInvitationsMsg(const QList<Invitation>& invitations) {
    QByteArray data = "INVITATIONS 0 ";
    for (const auto& invitation : invitations) {
        data.append(invitation.toByteArray());
        data.append(';');
    }
    return data;
}

void UDPManager::sendMessage(const QByteArray& data, const QHostAddress& addr, quint16 port) {
    MainServer::instance()->udpSocket->writeDatagram(data, addr, port);
}

void UDPManager::sendToUser(AbstractUser* user, const QByteArray& data) {
    MainServer::instance()->udpSocket->writeDatagram(data, user->getAddress(), user->getPort());
}

static quint32 seq;
bool UDPManager::recvPing(const QByteArray& data) {
    if (data[0] != '0') return false;

    QByteArrayList list = data.split(' ');
    quint32 _seq = list[1].toULongLong();
    if (_seq < seq) return false;

    seq = _seq;
    return true;
}